package com.wesley.growth.threadpool;

import java.util.Random;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicLong;

/**
 * ScheduledExecutorServiceTest
 *
 * @author WangPanYong
 * @since 2022/03/31 13:45
 */
public class ScheduledExecutorServiceTest {

    public static void main(String[] args)  {
//        testScheduleAtFixedRate();
        testScheduleWithFixedDelay(3);
    }

    static void testSchedule() throws ExecutionException, InterruptedException{
        ScheduledExecutorService scheduledExecutor = Executors.newSingleThreadScheduledExecutor(new ThreadFactoryImpl("TestScheduledThread"));

        // 提交的任务只会执行一次
        ScheduledFuture<Integer> scheduledFuture = scheduledExecutor.schedule(new Callable() {
            @Override
            public Integer call() {
                Thread thread = Thread.currentThread();
                System.out.println("我是延迟10s执行的一次性任务，线程name："+ thread.getName() + ",执行时间戳：" + System.currentTimeMillis() / 1000);
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                return 250;
            }
        }, 10L, TimeUnit.SECONDS);
        // scheduledFuture.get() 会阻塞调用此方法的线程，这里是 main 线程
        System.out.println("scheduledFuture.get():" + scheduledFuture.get());
        System.out.println("hello world");
        scheduledExecutor.shutdown();
    }

    static void testScheduleWithFixedDelay(int core) {
        // 只有一个核心线程，也就用这一个线程执行提交的延迟或者定时任务。
        ScheduledExecutorService scheduledExecutor = Executors.newScheduledThreadPool(core, new ThreadFactoryImpl("TestScheduledThread"));
        for (int i = 0; i < core; i++) {
            scheduledExecutor.scheduleWithFixedDelay(new DelayTask(), 0L, 2, TimeUnit.SECONDS);
        }
    }

    static class DelayTask implements Runnable {

        @Override
        public void run() {
            Thread thread = Thread.currentThread();
            System.out.println("我是每次执行完毕间隔2s执行的任务，线程name：" + thread.getName() + ",开始执行时间戳：" + System.currentTimeMillis() / 1000);
            // 随机数模拟每次执行耗时不同
            int random = new Random().nextInt(6);
            try {
                Thread.sleep(random * 1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("我是每次执行完毕间隔2s执行的任务，线程name：" + thread.getName() + ",执行完毕时间戳：" + System.currentTimeMillis() / 1000 + "\r\n");
        }
    }

    static void testScheduleAtFixedRate() {
        // 只有一个核心线程，也就用这一个线程执行提交的延迟或者定时任务。
        ScheduledExecutorService scheduledExecutor = Executors.newSingleThreadScheduledExecutor(new ThreadFactoryImpl("TestScheduledThread"));

        scheduledExecutor.scheduleAtFixedRate(() -> {
            Thread thread = Thread.currentThread();
            System.out.println("我是间隔1s执行的任务，线程name："+ thread.getName() + ",执行时间戳：" + System.currentTimeMillis() / 1000);
            try {
                // 模拟任务超时, 则后续的任务执行将会被推迟，不会出现两个任务同时执行。
                TimeUnit.SECONDS.sleep(5);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, 0L, 1, TimeUnit.SECONDS);

        // 延迟 3s 后再执行定时任务
        scheduledExecutor.scheduleAtFixedRate(() -> {
//            try {
                Thread thread = Thread.currentThread();
                System.out.println("我是间隔5s执行的任务，线程name：" + thread.getName() + ",执行时间戳：" + System.currentTimeMillis() / 1000);
                throw new RuntimeException(" ----> 运行时异常 ...");
//            } catch (Exception e) {
//                e.printStackTrace();
//            }
        }, 3L, 5, TimeUnit.SECONDS);
    }

    static class ThreadFactoryImpl implements ThreadFactory {
        private final AtomicLong threadIndex = new AtomicLong(0);
        private final String threadNamePrefix;
        private final boolean daemon;

        public ThreadFactoryImpl(final String threadNamePrefix) {
            this(threadNamePrefix, false);
        }

        public ThreadFactoryImpl(final String threadNamePrefix, boolean daemon) {
            this.threadNamePrefix = threadNamePrefix;
            this.daemon = daemon;
        }

        @Override
        public Thread newThread(Runnable r) {
            Thread thread = new Thread(r, threadNamePrefix +"-"+ this.threadIndex.incrementAndGet());
            thread.setDaemon(daemon);
            return thread;
        }
    }
}
